/**********************************************
* @file    	usart.c
* @author 	dralee
* @version	v1.0
* @date			2021-12-02
* @brief		串口1，中断方式，支持printf
* @attention 通过定义USE_USART_NO_x，x:[1,2,3,4,5]
*		可定义对应串口宏
***********************************************/

#include "usart.h"

#ifdef USE_USART_IRQ_RX
/*********************************************
* 串口接收标志 \r\n接收结束
* bit15: 接收完成标志
* bit14: 接收到0x0d
* bit13~0：接收到的有效字节数目
*********************************************/
uint16_t 	USARTx_RX_STA;
uint8_t		USARTx_RX_Buffer[RX_BUFFER_SIZE];			// 接收数据buffer
#endif

#ifdef USE_DMA_USART_TX // DMA->串口
uint8_t SendBuff[SENDBUF_SIZE]; // DMA发送到串口的缓冲区
#endif
#ifdef USE_DMA_USART_RX // 串口->DMA
uint8_t ReceiveBuff[RECEIVEBUFF_SIZE];						// 接收缓冲区
#endif

/**
 *****************************************
 * @brief 配置嵌套向量中断控制器NVIC
 * @param 无
 * @retval 无
 *****************************************/
static void NVIC_Configuration(void)
{
	NVIC_InitTypeDef NVIC_InitStruct;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 				// 嵌套向量中断控制器组选择，抢占2位，子优先2位
	NVIC_InitStruct.NVIC_IRQChannel = DBG_USART_IRQ;				// USART中断源
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;	// 抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;					// 子优先级
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;						// 使能中断
	NVIC_Init(&NVIC_InitStruct);														// 初始化配置NVIC	
}

/**
 *****************************************
 * @brief 配置USART IO口
 * @param 无
 * @retval 无
 *****************************************/
void USART_Config(void)
{
	GPIO_InitTypeDef	GPIO_InitStruct;
	USART_InitTypeDef	USART_InitStruct;
	
	DBG_USART_GPIO_APBxClockCmd(DBG_USART_GPIO_CLK, ENABLE); 	// GPIO口时钟
	DBG_USART_APBxClockCmd(DBG_USART_CLK, ENABLE);						// 串口外设时钟
	
	// 配置收发端口参数
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;							// TX为复用推挽输出
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;						// 速率50M
	GPIO_InitStruct.GPIO_Pin = DBG_USART_TX_GPIO_PIN;					// TX引脚
	GPIO_Init(DBG_USART_TX_GPIO_PORT, &GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;				// RX为浮空输入
	GPIO_InitStruct.GPIO_Pin = DBG_USART_RX_GPIO_PIN;					// RX引脚
	GPIO_Init(DBG_USART_RX_GPIO_PORT, &GPIO_InitStruct);
	
	// 配置串口参数
	USART_InitStruct.USART_BaudRate = DBG_USART_BAUDRATE;			// 波特率
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;	// 数据字长8位
	USART_InitStruct.USART_StopBits = USART_StopBits_1;				// 1个停止位
	USART_InitStruct.USART_Parity = USART_Parity_No;					// 无校验位
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	// 硬件流控制
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // 工作模式，收发
	USART_Init(DBG_USARTx, &USART_InitStruct);								// 初始化串口配置
	
	NVIC_Configuration();	// 串口中断优先级配置
	
#if defined(USE_USART_IRQ_RX) && !defined(USE_DMA_USART_RX)	// 开启中断接收且非DMA接收数据
	USART_ITConfig(DBG_USARTx, USART_IT_RXNE, ENABLE);				// 使能串口接收中断
#endif

	USART_ITConfig(DBG_USARTx, USART_IT_IDLE, ENABLE);				// 使能空闲中断

	USART_Cmd(DBG_USARTx, ENABLE);									// 使能串口
}

/**
 *****************************************
 * @brief 发送一个字节
 * @param USARTx 串口指针
 * @param ch 字节值
 * @retval 无
 *****************************************/
void USART_SendByte(USART_TypeDef *USARTx, uint8_t ch)
{
	USART_SendData(USARTx, ch); // 发送一个字节数据到USART
	while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET); // 等待发送数据寄存器为空
}

/**
 *****************************************
 * @brief 发送8位数组
 * @param USARTx 串口指针
 * @param array 8位数组
 * @param len 数组长度
 * @retval 无
 *****************************************/
void USART_SendArray(USART_TypeDef *USARTx, uint8_t *array, uint16_t len)
{
	uint8_t i;
	for(i = 0; i < len; ++i)
	{
		USART_SendByte(USARTx, array[i]);
	}
	while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); // 等待发送完成
}

/**
 *****************************************
 * @brief 发送字符串
 * @param USARTx 串口指针
 * @param str 发送的字符串指针
 * @retval 无
 *****************************************/
void USART_SendString(USART_TypeDef *USARTx, char *str)
{
	unsigned int i = 0;
	do
	{
		USART_SendByte(USARTx, *(str + i));
		i++;
	}while(*(str+i)!='\0');
	
	while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); // 等待发送完成
}

/**
 *****************************************
 * @brief 发送半个字，16位数
 * @param USARTx 串口指针
 * @param ch 半个字值
 * @retval 无
 *****************************************/
void USART_SendHalfWord(USART_TypeDef *USARTx, uint16_t ch)
{
	uint8_t ch_h, ch_l;
	ch_h = (ch & 0xFF00)>>8;  // 取出高8位
	ch_l = ch & 0xFF;					// 取出低8位
	
	USART_SendData(USARTx, ch_h); // 发送高8位
	while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET); // 等待发送寄存器为空
	
	USART_SendData(USARTx, ch_l); // 发送低8位
	while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET); // 等待发送寄存器为空
}

#ifdef USE_USART_IRQ_RX
/**
 *********************************************************
 * @brief 串口中断服务函数，为了方便复用，直接写到usart.c
 *		 没有统一维护至stm32f10x_it.c中
 * @param 无
 * @retval 无
 ********************************************************/
void DBG_USART_IRQHandler(void)
{
	uint8_t ch;
#ifdef USE_DMA_USART_RX
	uint16_t t;
#endif
	if(USART_GetITStatus(DBG_USARTx, USART_IT_RXNE) != RESET)
	{
		ch = USART_ReceiveData(DBG_USARTx); // 接收数据
		if((USARTx_RX_STA & 0x8000) == 0) // 接收未完成
		{
			if(USARTx_RX_STA & 0x4000) // 接收到0x0d
			{
				if(ch != 0x0a) USARTx_RX_STA = 0; // 接收错误，重新开始
				else USARTx_RX_STA |= 0x8000;			// 接收完成
			}
			else // 还没收到0x0d
			{
				if(ch == 0x0d) USARTx_RX_STA |= 0x4000;
				else
				{
					USARTx_RX_Buffer[USARTx_RX_STA & 0x3FFF] = ch; // 只算有效长度0x3FFF
					USARTx_RX_STA++;
					if(USARTx_RX_STA > (RX_BUFFER_SIZE - 1))
					{
						USARTx_RX_STA = 0;						// 数据错误，重新开始接收
					}
				}
			}
		}
#ifdef USE_USART_IRQ_RX_SEND_BACK		
		USART_SendData(DBG_USARTx, ch);     // 原样发送回去
#endif		
	}
	
	if(USART_GetITStatus(DBG_USARTx, USART_IT_IDLE) == SET) // 空闲中断
	{
#ifdef USE_DMA_USART_RX
		DMA_Cmd(USART_TX_DMA_CHANNEL, DISABLE);							// 关闭DMA传输
		t = DMA_GetCurrDataCounter(USART_TX_DMA_CHANNEL);				// 获取剩余数据数量
		USART_SendArray(DBG_USARTx, ReceiveBuff, RECEIVEBUFF_SIZE-t);	// 向电脑返回数据（接收数据数量=RECEIVEBUF_SIZE-剩余未传输数据量）
		DMA_SetCurrDataCounter(USART_TX_DMA_CHANNEL, RECEIVEBUFF_SIZE);	// 重新设置传输数据数量
		DMA_Cmd(USART_TX_DMA_CHANNEL, ENABLE);							// 开启DMA传输
		
		USART_ReceiveData(DBG_USARTx);									// 读一次数据，不然会一直进中断
		USART_ClearFlag(DBG_USARTx, USART_FLAG_IDLE);					// 清除串中空闲中断标志位
#endif
	}
}
#endif

/**
 **************************************************
 * @brief 读取中断接收到的数据，仅中断模式下有效
 * @param 无
 * @retval 接收到的数据 或 NULL
 *************************************************/
uint8_t* USART_GetData(void)
{
	if(USARTx_RX_STA & 0xC000) // 接收完成
	{
		USARTx_RX_STA = 0; // 已读，清空缓冲区
		return USARTx_RX_Buffer;
	}
	return NULL;
}

/**
 **************************************************
 * @brief 重定向C库函数，重定向后可使用printf函数
 * @param ch 发送的字节数据
 * @param f 文件指针
 * @retval 发送的数据
 *************************************************/
int fputc(int ch, FILE *f)
{
	USART_SendData(DBG_USARTx, (uint8_t)ch);	// 发送一个字节到串口
	while(USART_GetFlagStatus(DBG_USARTx, USART_FLAG_TXE) == RESET); // 等待发送寄存器为空
	return ch;
}

/**
 ***********************************************************
 * @brief 重定向C库函数，重定向后可使用scanf、getchar等函数
 * @param f 文件指针
 * @retval 发送的数据
 **********************************************************/
int fgetc(FILE *f)
{
	while(USART_GetFlagStatus(DBG_USARTx, USART_FLAG_RXNE) == RESET); // 等待串口输入
	return (int)USART_ReceiveData(DBG_USARTx);
}

#if defined(USE_DMA_USART_TX) || defined(USE_DMA_USART_RX)// 使用DMA
/**
 ***********************************************************
 * @brief USARTx TX DMA配置，内存到外设（USARTx-DR）
 * @param dir DMA到USARTx方向
 * @retval 无
 **********************************************************/
void USARTx_DMA_Config(USARTx_DMADirTypeDef dir)
{
	uint32_t USARTx_Buffer;
	uint32_t USXRTx_BufferSize;
	DMA_InitTypeDef DMA_InitStruct;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	// 开户DMA时钟
	DMA_InitStruct.DMA_PeripheralBaseAddr = USARTx_DR_ADDRESS; // 设置DMA源地址：串口数据寄存器地址
#ifdef USE_DMA_USART_TX
	USXRTx_BufferSize = dir == DMA_USARTx ? SENDBUF_SIZE : 0;
	USARTx_Buffer = dir == DMA_USARTx ? (uint32_t)SendBuff : NULL;
#endif
#ifdef USE_DMA_USART_RX
	USXRTx_BufferSize = dir == USARTx_DMA ? RECEIVEBUFF_SIZE : 0;
	USARTx_Buffer = dir == USARTx_DMA ? (uint32_t)ReceiveBuff : NULL;
#endif
	DMA_InitStruct.DMA_MemoryBaseAddr = USARTx_Buffer; //dir == USARTx_DMA ? (uint32_t)ReceiveBuff : (uint32_t)SendBuff;	// 内存地址（要传输的变量指针）
	DMA_InitStruct.DMA_DIR = dir == USARTx_DMA ? DMA_DIR_PeripheralSRC : DMA_DIR_PeripheralDST;					// 内存到外设
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据单位字节
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;		// 内存数据单位字节
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;		// 外设串口地址只有一个，不自增
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;						// 内存地址只增
	DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;												// 禁用内存到内存
	DMA_InitStruct.DMA_BufferSize = USXRTx_BufferSize;//USARTx_DMA ? RECEIVEBUF_SIZE : SENDBUF_SIZE;										// 传输的大小
	DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;							// 优先级中
	DMA_InitStruct.DMA_Mode = dir == USARTx_DMA ? DMA_Mode_Circular : DMA_Mode_Normal;											// 传输模式，接收设置为循环，发送设置为单次
	DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStruct);								// 初始化DMA
	
	DMA_Cmd(USART_TX_DMA_CHANNEL, ENABLE);													// 使能DMA

}
#endif
